iT邦幫忙

2026 iThome 鐵人賽

DAY 3
1

先看數字

環境架好了,先跑內建的壓測工具,有數字再談原理:

docker exec redis30days redis-benchmark -t set,get -n 100000 -q (-n 送 10 萬次請求,-q 只印結果)

https://ithelp.ithome.com.tw/upload/images/20260916/20184209jwAJ2PZ4MJ.png

每秒 24 萬次寫、26 萬次讀,一半以上的請求 0.1 毫秒上下就回來了。

工作上的 API 大概 50 毫秒回來就算不錯。當然那是一整趟請求(查 DB、跑邏輯、組回傳),跟這裡單一個 GET 不能直接比,但跟 Redis 還是差很大!

記憶體

第一個原因是資料都放在記憶體裡。

存取來源 大約耗時
記憶體 100 奈秒
SSD 隨機讀 100 微秒(1,000 倍)
傳統硬碟尋軌 10 毫秒(100,000 倍)

MySQL 也有 Buffer Pool 在記憶體裡放資料,但每個查詢還是得經過 SQL 解析、交易和鎖的管理,記憶體對它來說只是快取。Redis 反過來,記憶體就是它的家,持久化才是額外做的事(之後會學持久化)。

資料結構

第二個原因是每個型別底層都為特定操作優化過。

LPUSH 往 List 開頭插入是 O(1),SISMEMBER 判斷在不在集合裡也是 O(1)。同樣的事在關聯式資料庫裡都得繞索引一圈。

之後講到型別會順便看它底層是什麼。

單執行緒

第三個原因最酷,Redis 處理指令是單執行緒的。

只有一條執行緒代表沒有鎖競爭、沒有 context switch、也沒有多執行緒的同步成本。反正瓶頸本來就不在 CPU,在記憶體和網路。

那上萬個連線怎麼辦?靠 I/O 多工(epoll),一條執行緒同時盯著所有連線,誰有資料進來就處理誰。

單執行緒還有一個副作用:每個指令天然就是原子的。 不用加鎖、不用 synchronized,INCR 就是不會少加。

那什麼時候慢?

單執行緒讓指令天然原子,但代價是一個慢指令會卡住所有人

KEYS * 會把所有 key 撈出來,有幾筆就得掃幾筆。資料少的時候看不出來,先灌 100 萬筆進去:

# --pipe 批次送,一筆一筆打要跑很久
docker exec -i redis30days sh -c \
"awk 'BEGIN{for(i=0;i<1000000;i++) printf \"SET key:%d v%d\r\n\", i, i}' | redis-cli -n 1 --pipe"

接著開兩個終端機。A 只做一件事,不停送 PING 量往返時間:

docker exec -it redis30days redis-cli --latency

B 去跑 KEYS *

docker exec redis30days redis-cli -n 1 KEYS '*'

A 的那行數字會即時更新,B 一打下去就看得到:

B 還沒動作
https://ithelp.ithome.com.tw/upload/images/20260916/20184209eayr18fPmz.png
B 跑了一次 KEYS *
https://ithelp.ithome.com.tw/upload/images/20260916/20184209RlzC3xBhwS.png

A 從頭到尾只是在送 PING,什麼都沒做錯,卻有一次等了 108 毫秒才拿到回應,因為 Redis 那條唯一的執行緒正忙著 B 的 KEYS *,誰都插不了隊。

順便看一下 avg,只從 0.12 變成 0.24 毫秒,低到完全看不出問題,但 max 跳到了 108。一千多次取樣裡就那麼一次,監控看平均一切正常,但那個使用者是真的等了 108 毫秒。

用前面 26 萬 QPS 換算,那 108 毫秒本來可以處理將近三萬個請求。

正式環境不要用 KEYS,要掃 key 請用 SCAN 分批拿。 大 Key 也會有一樣的問題,留到之後會遇到。

明天

原理講完了,明天從最簡單的 String 開始/images/emoticon/emoticon29.gif


上一篇
Day 2|環境準備
下一篇
Day 4|String
系列文
從購物車到秒殺:30 天 Redis 高併發自學筆記5
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言